Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/pages/token/[id].tsx
1450 views
1
/*
2
* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Visit
8
9
https://cocalc.com/token/vZmCKcIMha2nKyFQ0rgK
10
11
to carry out the action associated with the token vZmCKcIMha2nKyFQ0rgK.
12
*/
13
14
import Footer from "components/landing/footer";
15
import Head from "components/landing/head";
16
import Header from "components/landing/header";
17
import { Customize, CustomizeType } from "lib/customize";
18
import withCustomize from "lib/with-customize";
19
import useAPI from "lib/hooks/api";
20
import { Alert, Button, Card, Divider, Layout, Space, Spin } from "antd";
21
import { useRouter } from "next/router";
22
import type { Description } from "@cocalc/util/db-schema/token-actions";
23
import { capitalize } from "@cocalc/util/misc";
24
import { useEffect, useState } from "react";
25
import { getTokenDescription } from "@cocalc/server/token-actions/handle";
26
import Markdown from "@cocalc/frontend/editors/slate/static-markdown";
27
import { Icon, IconName } from "@cocalc/frontend/components/icon";
28
import getAccountId from "lib/account/get-account";
29
import InPlaceSignInOrUp from "components/auth/in-place-sign-in-or-up";
30
import StripePayment from "@cocalc/frontend/purchases/stripe-payment";
31
import { LineItemsTable } from "@cocalc/frontend/purchases/line-items";
32
import A from "components/misc/A";
33
34
const STYLE = { margin: "30px auto", maxWidth: "750px", fontSize: "14pt" };
35
36
export async function getServerSideProps(context) {
37
const { id: token_id } = context.params;
38
const account_id = await getAccountId(context.req);
39
let description;
40
try {
41
description = await getTokenDescription(token_id, account_id);
42
} catch (error) {
43
description = {
44
type: "error",
45
title: "Error",
46
details: `${error}`,
47
cancelText: "",
48
okText: "OK",
49
};
50
}
51
return await withCustomize({ context, props: { token_id, description } });
52
}
53
54
function reloadOnceToCheckForAuth() {
55
const now = Date.now();
56
const last = localStorage.reloadOnceToCheckForAuth;
57
if (last && now - last <= 10000) {
58
return;
59
}
60
localStorage.reloadOnceToCheckForAuth = now;
61
location.reload();
62
}
63
64
interface Props {
65
customize: CustomizeType;
66
token_id: string;
67
description: Description & {
68
title?: string;
69
details?: string;
70
okText?: string;
71
cancelText?: string;
72
icon?: IconName;
73
signIn?: boolean;
74
};
75
}
76
77
export default function TokenActions({
78
customize,
79
description,
80
token_id,
81
}: Props) {
82
const router = useRouter();
83
const [doAction, setDoAction] = useState<boolean>(false);
84
const [loading, setLoading] = useState<boolean>(false);
85
const title = getTitle(description);
86
useEffect(() => {
87
// due to samesite auth cookie, we refresh browser once...
88
// This is needed when clicking a URL from outside cocalc, which is likely.
89
if (description.signIn) {
90
reloadOnceToCheckForAuth();
91
}
92
}, []);
93
94
return (
95
<Customize value={customize}>
96
<Head title={title} />
97
<Layout>
98
<Header />
99
{!!description.signIn && (
100
<div style={{ marginTop: "30px" }}>
101
<InPlaceSignInOrUp
102
title={"Please create an account (which is very easy) or sign in"}
103
/>
104
</div>
105
)}
106
<Dialog
107
disabled={doAction || !!description.signIn}
108
loading={loading}
109
title={title}
110
details={description.details}
111
okText={description.okText}
112
cancelText={description.cancelText}
113
icon={description.icon}
114
payment={description["payment"]}
115
onConfirm={() => {
116
setDoAction(true);
117
}}
118
onCancel={() => {
119
setLoading(true);
120
router.push("/");
121
}}
122
/>
123
{!description.signIn && doAction && <HandleToken token={token_id} />}
124
<Footer />
125
</Layout>
126
</Customize>
127
);
128
}
129
130
function Dialog({
131
disabled,
132
title,
133
details,
134
okText,
135
cancelText,
136
icon,
137
onConfirm,
138
onCancel,
139
loading,
140
payment,
141
}) {
142
return (
143
<Card
144
style={{
145
margin: "30px auto",
146
width: "750px",
147
maxWidth: "100%",
148
}}
149
title={
150
<Space>
151
{icon && <Icon name={icon} />}
152
<Markdown value={title} style={{ marginBottom: "-1em" }} />
153
</Space>
154
}
155
>
156
{payment && <LineItemsTable lineItems={payment.lineItems} />}
157
{details && <Markdown value={details} style={{ marginTop: "30px" }} />}
158
<Divider />
159
<div style={{ textAlign: "center" }}>
160
<Space style={{ marginTop: "8px" }}>
161
{loading && <Spin />}
162
{cancelText != "" && (
163
<Button
164
size="large"
165
onClick={onCancel}
166
disabled={disabled || loading}
167
>
168
{cancelText ?? "Cancel"}
169
</Button>
170
)}
171
{okText != "" && (
172
<Button
173
size="large"
174
onClick={onConfirm}
175
disabled={disabled || loading}
176
type="primary"
177
>
178
{okText ?? "Confirm"}
179
</Button>
180
)}
181
</Space>
182
</div>
183
</Card>
184
);
185
}
186
187
function HandleToken({ token }) {
188
const { calling, result, error } = useAPI("token-action", { token });
189
190
return (
191
<div>
192
{calling && (
193
<div style={{ ...STYLE, textAlign: "center" }}>
194
<Spin />
195
</div>
196
)}
197
{error && <Alert showIcon style={STYLE} type="error" message={error} />}
198
{!calling && result != null && !error && (
199
<RenderResult data={result.data} />
200
)}
201
</div>
202
);
203
}
204
205
function RenderResult({ data }) {
206
const [finishedPaying, setFinishedPaying] = useState<boolean>(false);
207
208
if (data?.pay != null && !finishedPaying) {
209
return (
210
<Card style={STYLE} title="Make a Payment">
211
<div>
212
<StripePayment
213
disabled={finishedPaying}
214
{...data.pay}
215
onFinished={() => {
216
setFinishedPaying(true);
217
}}
218
/>
219
</div>
220
</Card>
221
);
222
} else {
223
return (
224
<Alert
225
showIcon
226
style={STYLE}
227
type="success"
228
message={
229
<>
230
Thank you for your payment! Please visit{" "}
231
<A href="/settings/payments">the payments page</A> to ensure your
232
payment is completed successfully and download a receipt.
233
</>
234
}
235
description={data?.text ? <Markdown value={data?.text} /> : undefined}
236
/>
237
);
238
}
239
}
240
241
function getTitle({ title, type }: Description & { title?: string }) {
242
if (title) {
243
return title;
244
}
245
switch (type) {
246
case "make-payment":
247
return "Make a Payment";
248
case "disable-daily-statements":
249
return "Disable Daily Statements";
250
default:
251
if (typeof type == "string" && type) {
252
return capitalize(type.replace(/-/g, " "));
253
}
254
return "Token Action";
255
}
256
}
257
258